package com.gitee.linmt.service;

import com.gitee.linmt.entity.Role;
import com.gitee.linmt.entity.UserInfo;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;

/**
 * 自定义 UserDetailService，根据用户名获取用户信息.
 *
 * @author <a href="mailto:lin-mt@outlook.com">lin-mt</a>
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
    
    private static final Set<String> USERS;
    
    private static final User.UserBuilder USER_BUILDER;
    
    static {
        USER_BUILDER = User.builder()
                .passwordEncoder(s -> PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(s));
        USERS = new HashSet<>();
        USERS.add("user");
        USERS.add("admin");
    }
    
    @Override
    public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
        if (USERS.contains(username)) {
            // 基本很少使用该方式，但是有些坑还是说明下的好
            final UserDetails userDetails = USER_BUILDER.username("user").password("password")
                    // 设置认证信息，如果该信息是以 ROLE_ 开头的则可以理解为角色
                    .authorities("readUserInfo")
                    // 角色，此处会自动添加 ROLE_ 前缀（不能以 ROLE_ 开头，否则会抛出异常 IllegalArgumentException）
                    .roles("user")
                    // 设置权限或者角色都是以最后一次调用设置 authorities 属性为准，点进去看下源码会比较清晰
                    // 该例子中的 roles("user") 会覆盖上一步 authorities("readUserInfo") 的值
                    // 也就是最后构建出来的 userDetails 只有 ROLE_user 的权限，没有 readUserInfo 的权限
                    .build();
            
            // 大部分是以下面这种方式获取用户信息，可以自己通过各种 ORM 框架从数据库中读取哈
            final UserInfo userInfo = new UserInfo();
            userInfo.setId(1L);
            userInfo.setUsername(username);
            // 此处需要自己实现自己的需求，从数据库或其他地方获取用户的权限
            // 暂时就将拥有的权限设置为用户名，为什么我这里不说是设置角色呢，后面我不懒的话会有博客说明
            final Set<Role> roles = new HashSet<>();
            roles.add(new Role().setRoleName("ROLE_" + username));
            userInfo.setAuthorities(roles);
            // 我们在注册用户加密密码的时候，使用的方式要跟我们注入 Spring Security 的方式相同
            userInfo.setPassword(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("password"));
            return userInfo;
        }
        throw new UsernameNotFoundException("该用户不存在");
    }
}